package com.cmy.framework_widget.wheelview

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View

class WheelView : View {
    //总体宽度、高度、Item的高度
    private var widthView = 0
    private var heightView = 0
    private var itemHeight = 0

    //需要显示的行数
    private var showCount = SHOW_COUNT

    /**
     * 得到当前的选择项
     */
    //当前默认选择的位置
    var selectItem = SELECT
        private set

    //字体颜色、大小、补白
    private var fontColor = FONT_COLOR
    private var fontSize = FONT_SIZE
    private var padding = PADDING

    //文本列表
    private var lists: List<String>? = null

    //选中项的辅助文本，可为空
    private var selectTip: String? = null

    //每一项Item和选中项
    private val wheelItems: MutableList<WheelItem> = ArrayList()
    private var wheelSelect: WheelSelect? = null

    //手点击的Y坐标
    private var mTouchY = 0f

    //监听器
    private var listener: OnWheelViewItemSelectListener? = null

    constructor(context: Context?) : super(context) {}
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
    }

    /**
     * 设置字体的颜色，不设置的话默认为黑色
     * @param fontColor
     * @return
     */
    fun fontColor(fontColor: Int): WheelView {
        this.fontColor = fontColor
        return this
    }

    /**
     * 设置字体的大小，不设置的话默认为30
     * @param fontSize
     * @return
     */
    fun fontSize(fontSize: Int): WheelView {
        this.fontSize = fontSize
        return this
    }

    /**
     * 设置文本到上下两边的补白，不合适的话默认为10
     * @param padding
     * @return
     */
    fun padding(padding: Int): WheelView {
        this.padding = padding
        return this
    }

    /**
     * 设置选中项的复制文本，可以不设置
     * @param selectTip
     * @return
     */
    fun selectTip(selectTip: String?): WheelView {
        this.selectTip = selectTip
        return this
    }

    /**
     * 设置文本列表，必须且必须在build方法之前设置
     * @param lists
     * @return
     */
    fun lists(lists: List<String>?): WheelView {
        this.lists = lists
        return this
    }

    /**
     * 设置显示行数，不设置的话默认为3
     * @param showCount
     * @return
     */
    fun showCount(showCount: Int): WheelView {
        /*if(showCount % 2 == 0){
            throw new IllegalStateException("the showCount must be odd");
        }*/
        this.showCount = showCount
        return this
    }

    /**
     * 设置默认选中的文本的索引，不设置默认为0
     * @param select
     * @return
     */
    fun select(select: Int): WheelView {
        selectItem = select
        return this
    }

    /**
     * 最后调用的方法，判断是否有必要函数没有被调用
     * @return
     */
    fun build(): WheelView {
        checkNotNull(lists) { "this method must invoke after the method [lists]" }
        return this
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //得到总体宽度
        widthView = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight
        // 得到每一个Item的高度
        val mPaint = Paint()
        mPaint.textSize = fontSize.toFloat()
        val metrics = mPaint.fontMetrics
        //        itemHeight = (int) (metrics.bottom - metrics.top) + 2 * padding;
        itemHeight = dip2px(50)
        //初始化每一个WheelItem
        initWheelItems(widthView, itemHeight)
        //初始化WheelSelect
        wheelSelect = WheelSelect(
            showCount / 2 * itemHeight,
            widthView,
            itemHeight,
            selectTip,
            fontColor,
            fontSize,
            padding
        )
        //得到所有的高度
        heightView = itemHeight * showCount
        super.onMeasure(widthView, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
    }

    /**
     * 创建显示个数+2个WheelItem
     * @param width
     * @param itemHeight
     */
    private fun initWheelItems(width: Int, itemHeight: Int) {
        wheelItems.clear()
        for (i in 0 until showCount + 2) {
            val startY = itemHeight * (i - 1)
            var stringIndex = selectItem - showCount / 2 - 1 + i
            if (stringIndex < 0) {
                stringIndex = lists!!.size + stringIndex
            }
            wheelItems.add(
                WheelItem(
                    startY.toFloat(),
                    width,
                    itemHeight,
                    fontColor,
                    fontSize,
                    lists!![stringIndex]
                )
            )
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mTouchY = event.y
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                val dy = event.y - mTouchY
                mTouchY = event.y
                handleMove(dy)
            }
            MotionEvent.ACTION_UP -> handleUp()
        }
        return super.onTouchEvent(event)
    }

    /**
     * 处理移动操作
     * @param dy
     */
    private fun handleMove(dy: Float) {
        //调整坐标
        for (item in wheelItems) {
            item.adjust(dy)
        }
        invalidate()
        //调整
        adjust()
    }

    /**
     * 处理抬起操作
     */
    private fun handleUp() {
        var index = -1
        //得到应该选择的那一项
        for (i in wheelItems.indices) {
            val item = wheelItems[i]
            //如果startY在selectItem的中点上面，则将该项作为选择项
            if (item.startY > wheelSelect!!.startY && item.startY < wheelSelect!!.startY + itemHeight / 2) {
                index = i
                break
            }
            //如果startY在selectItem的中点下面，则将上一项作为选择项
            if (item.startY >= wheelSelect!!.startY + itemHeight / 2 && item.startY < wheelSelect!!.startY + itemHeight) {
                index = i - 1
                break
            }
        }
        //如果没找到或者其他因素，直接返回
        if (index == -1) {
            return
        }
        //得到偏移的位移
        val dy = wheelSelect!!.startY - wheelItems[index].startY
        //调整坐标
        for (item in wheelItems) {
            item.adjust(dy)
        }
        invalidate()
        // 调整
        adjust()
        //设置选择项
        val stringIndex = lists!!.indexOf(wheelItems[index].text)
        if (stringIndex != -1) {
            selectItem = stringIndex
            if (listener != null) {
                listener!!.onItemSelect(selectItem)
            }
        }
    }

    /**
     * 调整Item移动和循环显示
     */
    private fun adjust() {
        //如果向下滑动超出半个Item的高度，则调整容器
        if (wheelItems[0].startY >= -itemHeight / 2) {
            //移除最后一个Item重用
            val item = wheelItems.removeAt(wheelItems.size - 1)
            //设置起点Y坐标
            item.startY = wheelItems[0].startY - itemHeight
            //得到文本在容器中的索引
            var index = lists!!.indexOf(wheelItems[0].text)
            if (index == -1) {
                return
            }
            index -= 1
            if (index < 0) {
                index = lists!!.size + index
            }
            //设置文本
            item.text = lists!![index]
            //添加到最开始
            wheelItems.add(0, item)
            invalidate()
            return
        }

        //如果向上滑超出半个Item的高度，则调整容器
        if (wheelItems[0].startY <= -itemHeight / 2 - itemHeight) {
            //移除第一个Item重用
            val item = wheelItems.removeAt(0)
            //设置起点Y坐标
            item.startY = wheelItems[wheelItems.size - 1].startY + itemHeight
            //得到文本在容器中的索引
            var index = lists!!.indexOf(wheelItems[wheelItems.size - 1].text)
            if (index == -1) {
                return
            }
            index += 1
            if (index >= lists!!.size) {
                index = 0
            }
            //设置文本
            item.text = lists!![index]
            //添加到最后面
            wheelItems.add(item)
            invalidate()
            return
        }
    }

    override fun onDraw(canvas: Canvas) {


        //绘制每一项Item
        for (item in wheelItems) {
            item.onDraw(canvas)
        }
        //绘制阴影
        if (wheelSelect != null) {
            wheelSelect!!.onDraw(canvas)
        }
    }

    /**
     * 设置监听器
     * @param listener
     * @return
     */
    fun listener(listener: OnWheelViewItemSelectListener?): WheelView {
        this.listener = listener
        return this
    }

    interface OnWheelViewItemSelectListener {
        fun onItemSelect(index: Int)
    }

    /**
     * dp转px
     *
     * @param dp
     * @return
     */
    private fun dip2px(dp: Int): Int {
        val scale = context.resources.displayMetrics.density
        return (scale * dp + 0.5f).toInt()
    }

    companion object {
        const val FONT_COLOR = Color.BLACK
        const val FONT_SIZE = 30
        const val PADDING = 10
        const val SHOW_COUNT = 3
        const val SELECT = 0
    }
}